# encoding: utf-8
from __future__ import absolute_import, unicode_literals

import datetime
import hashlib
import random
import time
import json
from hashlib import md5
from socket import socket

from django.conf import settings
import requests
from xml.etree import ElementTree as ET
from cool.views import ViewSite, CoolAPIException, ErrorCode
from cool.views.fields import SplitCharField
from rest_framework import fields
from constance import config
from django.db import transaction
from django.db.models import Q
from rest_framework.response import Response

from apps.wechat import biz
from apps.bookings import plc
from core import utils, constants as core_constants
from core.utils import APIBase

from . import models, serializer, constants
from ..meetings.views import BaseView
from ..wechat.views import UserBaseView

site = ViewSite(name='bookings', app_name='bookings')


@site
class CourtCreate(BaseView):
    name = "创建篮球场"
    response_info_serializer_class = serializer.CourtSerializer

    def get_context(self, request, *args, **kwargs):
        court = models.Court.objects.create(
            name=request.params.name,
            description=request.params.description,
            create_user_id=request.user.pk,
        )
        try:
            court.qr_code = biz.get_wxa_code_unlimited_file(
                "court_%d.jpg" % court.pk, scene="court_id=%d" % court.pk,
                page="pages/court/detail"
            )
            court.save(update_fields=['qr_code', ], force_update=True)
        except Exception:
            utils.exception_logging.exception("get_wxa_code_unlimited_file", extra={'request': request})
        self.get_court_follow(court.pk, request.user.pk)
        return serializer.CourtSerializer(court, request=request).data

    class Meta:
        param_fields = (
            ('name', fields.CharField(label='名称', max_length=64)),
            ('description', fields.CharField(label='描述', max_length=255)),
        )


class CourtBase(BaseView):

    def check_api_permissions(self, request, *args, **kwargs):
        super(CourtBase, self).check_api_permissions(request, *args, **kwargs)
        court = models.Court.objects.filter(pk=request.params.court_id).first()
        if court is None:
            raise CoolAPIException(ErrorCode.ERROR_BAD_PARAMETER)
        setattr(self, 'court', court)

    def get_context(self, request, *args, **kwargs):
        raise NotImplementedError

    class Meta:
        path = '/'
        param_fields = (
            ('court_id', fields.IntegerField(label='场地编号')),
        )


@site
class CourtEdit(CourtBase):
    name = "修改篮球场"
    response_info_serializer_class = serializer.CourtSerializer

    def get_context(self, request, *args, **kwargs):
        self.court.name = request.params.name
        self.court.description = request.params.description
        update_fields = ['name', 'description']
        self.court.save(force_update=True, update_fields=update_fields)
        return serializer.CourtSerializer(self.court, request=request).data

    class Meta:
        param_fields = (
            ('name', fields.CharField(label='名称', max_length=64)),
            ('description', fields.CharField(label='描述', max_length=255, required=False, default="")),
        )


@site
class CourtDelete(CourtBase):
    name = "删除篮球场"

    def get_context(self, request, *args, **kwargs):
        self.court.delete()
        return {}


@site
class CourtInfo(CourtBase):
    name = "场地信息"
    response_info_serializer_class = serializer.CourtDetailSerializer

    def get_context(self, request, *args, **kwargs):
        ret = self.get_date_time_settings()
        now_time = time.strftime("%H:%M:%S", time.localtime())
        start_time = str(ret['start_time'])
        end_time = str(ret['end_time'])
        if start_time < now_time < end_time:
            court = models.Court.objects.filter(pk=request.params.court_id).first()
            if config.IS_PRODUCTION:
                # 从plc度灯光状态
                result = plc.send_message(config.PLC_IP_ADDRESS, int(request.params.court_id), 'QUERY',
                                          int(config.PLC_TOTAL))
                if result[1] == 'ON':
                    court.light = 1
                else:
                    court.light = 0

            return serializer.CourtDetailSerializer(court, request=request).data
        else:
            raise CoolAPIException(ErrorCode.ERROR_PERMISSION)


@site
class Off(CourtBase):
    name = "关灯"

    def get_context(self, request, *args, **kwargs):
        # 连接plc进行关灯
        if config.IS_PRODUCTION:
            try:
                result = plc.send_message(config.PLC_IP_ADDRESS, self.court.id, 'OFF', int(config.PLC_TOTAL))
            except:
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)
            if result[0] != 'SUCCESS' or result[1] != 'OFF':
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)

        self.court.light = 0
        update_fields = ['light', ]
        self.court.save(force_update=True, update_fields=update_fields)


@site
class CloseAll(BaseView):
    name = "一键关灯"

    def get_context(self, request, *args, **kwargs):
        # 连接plc进行一键关灯
        if config.IS_PRODUCTION:
            try:
                result = plc.send_message(config.PLC_IP_ADDRESS, 10000, 'OFF', int(config.PLC_TOTAL))
            except:
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)
            if result[0] != 'SUCCESS' or result[1] != 'ALL_OFF':
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)

        models.Court.objects.filter(light=1, usage=1).update(light='0')


@site
class OpenAll(BaseView):
    name = "一键开灯"

    def get_context(self, request, *args, **kwargs):
        # 连接plc进行一键开灯
        if config.IS_PRODUCTION:
            try:
                result = plc.send_message(config.PLC_IP_ADDRESS, 10000, 'ON', int(config.PLC_TOTAL))
            except:
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)

            if result[0] != 'SUCCESS' or result[1] != 'ALL_ON':
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)
        models.Court.objects.filter(light=0, usage=1).update(light='1')


@site
class FollowCourts(BaseView):
    name = "已关注篮球场列表"
    response_info_serializer_class = serializer.CourtSerializer
    response_many = True

    def get_context(self, request, *args, **kwargs):
        courts = models.Court.objects.filter(
            follows__user_id=request.user.pk, follows__delete_status=core_constants.DeleteCode.NORMAL.code
        )
        return serializer.CourtSerializer(courts, request=request, many=True).data


@site
class CourtsList(BaseView):
    name = "篮球场预约系统"
    response_info_serializer_class = serializer.CourtSerializer
    response_many = True

    def get_context(self, request, *args, **kwargs):
        courts = models.Court.objects.filter(usage=1
                                             )
        # 连接plc进行状态查询，返回状态数组
        if config.IS_PRODUCTION:
            try:
                result_lists = plc.send_message(config.PLC_IP_ADDRESS, 10000, 'QUERY', int(config.PLC_TOTAL))
            except:
                raise CoolAPIException(ErrorCode.ERROR_SYSTEM)
            # print(result_lists)
            for i, val in enumerate(result_lists):
                if len(courts) > i:
                    courts[i].light = val
                else:
                    break
        return serializer.CourtSerializer(courts, request=request, many=True).data


@site
class CreateCourts(BaseView):
    name = "创建篮球场列表"
    response_info_serializer_class = serializer.CourtSerializer
    response_many = True

    def get_context(self, request, *args, **kwargs):
        courts = models.Court.objects.filter(create_user_id=request.user.pk)
        return serializer.CourtSerializer(courts, request=request, many=True).data


@site
class OwnPlc(CourtBase):
    name = "是否控制该plc"

    response_info_serializer_class = serializer.BookingDetailSerializer

    def check_api_permissions(self, request, *args, **kwargs):
        super(UserBaseView, self).check_api_permissions(request, *args, **kwargs)
        if not isinstance(request.user, models.User):
            raise CoolAPIException(ErrorCode.ERR_WECHAT_LOGIN)

    def get_context(self, request, *args, **kwargs):
        d = datetime.date.today()
        booking = models.Booking.objects.filter(court_id=request.params.court_id, user_id=request.user.pk,
                                                end_time=None, date=d).first()
        if booking is None:
            raise CoolAPIException(ErrorCode.ERROR_PERMISSION)

        return serializer.BookingDetailSerializer(booking, request=request).data


@site
class CourtBookings(BaseView):
    name = "篮球场预约列表"

    @classmethod
    def response_info_data(cls):
        from cool.views.utils import get_serializer_info
        ret = cls.response_info_date_time_settings()
        ret.update({
            'courts': get_serializer_info(serializer.CourtSerializer(), True),
            'bookings': get_serializer_info(serializer.BookingSerializer(), True)
        })
        return ret

    def get_context(self, request, *args, **kwargs):
        if len(request.params.court_ids) > 10:
            raise CoolAPIException(ErrorCode.ERROR_BAD_PARAMETER)
        d = datetime.date.today()
        if request.params.date is not None:
            d = request.params.date
        courts = list(sorted(models.Court.objects.filter(
            id__in=request.params.court_ids), key=lambda x: request.params.court_ids.index(x.id)
        ))
        bookings = models.Booking.objects.filter(court_id__in=request.params.court_ids,
                                                 date=d).order_by('start_time')
        ret = self.get_date_time_settings()
        ret.update({
            'courts': serializer.CourtSerializer(courts, request=request, many=True).data,
            'bookings': serializer.BookingSerializer(bookings, request=request, many=True).data
        })
        return ret

    class Meta:
        param_fields = (
            ('court_ids', SplitCharField(label='篮球场ID列表', sep=',', child=fields.IntegerField())),
            ('date', utils.DateField(label='日期', required=False, default=None)),
        )


@site
class MyOpt(BaseView):
    name = "我的操作记录"

    def get_context(self, request, *args, **kwargs):
        bookings = list(models.Booking.objects.filter(
            user_id=request.user.pk
        ).order_by('-start_time'))
        return serializer.BookingDetailSerializer(bookings, request=request, many=True).data


@site
class MyBookings(BaseView):
    name = "我参与的预订列表"

    # def check_api_permissions(self, request, *args, **kwargs):
    #     super(UserBaseView, self).check_api_permissions(request, *args, **kwargs)
    #     if not isinstance(request.user, models.User):
    #         raise CoolAPIException(ErrorCode.ERR_WECHAT_LOGIN)

    @classmethod
    def response_info_data(cls):
        from cool.views.utils import get_serializer_info
        ret = cls.response_info_date_time_settings()
        ret.update({
            'courts': get_serializer_info(serializer.CourtSerializer(), True),
            'bookings': get_serializer_info(serializer.BookingSerializer(), True)
        })
        return ret

    def get_context(self, request, *args, **kwargs):
        d = datetime.date.today()
        if request.params.date is not None:
            d = request.params.date
        bookings = list(models.Booking.objects.filter(
            user_id=request.user.pk, date=d
        ))
        courts = list(
            models.Court.objects.filter(id__in=set(map(lambda x: x.court_id, bookings))))
        ret = self.get_date_time_settings()
        ret.update({
            'courts': serializer.CourtSerializer(courts, request=request, many=True).data,
            'bookings': serializer.BookingSerializer(bookings, request=request, many=True).data
        })
        return ret

    class Meta:
        param_fields = (
            ('date', utils.DateField(label='日期', required=False, default=None)),
        )


@site
class Reserve(CourtBase):
    name = "开灯操作"
    response_info_serializer_class = serializer.BookingDetailSerializer

    def check_api_permissions(self, request, *args, **kwargs):
        super(UserBaseView, self).check_api_permissions(request, *args, **kwargs)
        if not isinstance(request.user, models.User):
            raise CoolAPIException(ErrorCode.ERR_WECHAT_LOGIN)

    @staticmethod
    def time_ok(t):
        return t.second == 0 and t.microsecond == 0 and t.minute in (0, 30)

    def get_context(self, request, *args, **kwargs):
        now = datetime.datetime.now()
        with transaction.atomic():
            # if models.Booking.objects.filter().select_for_update().exists():
            #     raise CoolAPIException(ErrorCode.ERR_MEETING_ROOM_INUSE)
            booking = models.Booking.objects.create(
                user_id=request.user.pk,
                court_id=request.params.court_id,
                date=datetime.date.today(),
                start_time=now.strftime("%Y-%m-%d %H:%M:%S"),
            )

            # 连接plc进行开灯
            if config.IS_PRODUCTION:
                try:
                    result = plc.send_message(config.PLC_IP_ADDRESS, request.params.court_id, 'ON',
                                              int(config.PLC_TOTAL))
                except:
                    print(result)
                    raise CoolAPIException(ErrorCode.ERROR_SYSTEM)

                if result[0] != 'SUCCESS' or result[1] != 'ON':
                    raise CoolAPIException(ErrorCode.ERROR_SYSTEM)
            # 开灯，把状态置为’1‘
            court = models.Court.objects.filter(pk=request.params.court_id).first()
            court.__setattr__('light', 1),
            court.save(force_update=True, update_fields=['light', ])
        return serializer.BookingDetailSerializer(booking, request=request).data

    class Meta:
        param_fields = (
            ('court_id', fields.IntegerField(label='篮球场ID')),
            # ('plc_no', fields.IntegerField(label='plc编号')),
            # ('name', fields.CharField(label='名称', max_length=64)),
            # ('description', fields.CharField(label='描述', max_length=255, required=False, default="")),
            # ('date', fields.DateField(label='预定日期')),
            # ('start_time', fields.TimeField(label='开始时间')),
            # ('end_time', fields.TimeField(label='结束时间')),
        )


class BookingBase(BaseView):
    response_info_serializer_class = serializer.BookingDetailSerializer

    def check_api_permissions(self, request, *args, **kwargs):
        super(BookingBase, self).check_api_permissions(request, *args, **kwargs)
        booking = models.Booking.objects.filter(pk=request.params.booking_id).first()
        if booking is None:
            raise CoolAPIException(ErrorCode.ERROR_BAD_PARAMETER)
        setattr(self, 'booking', booking)

    def get_context(self, request, *args, **kwargs):
        raise NotImplementedError

    class Meta:
        path = '/'
        param_fields = (
            ('booking_id', fields.IntegerField(label='预订ID')),
        )


@site
class Info(BookingBase):
    name = "预订详情"

    def get_context(self, request, *args, **kwargs):
        return serializer.BookingDetailSerializer(self.booking, request=request).data


@site
class PlcStatus(BookingBase):
    name = "plc状态"

    def get_context(self, request, *args, **kwargs):
        # 从数据库获取plc状态，后面会直接与plc通信进行状态查询
        court = models.Court.objects.filter(pk=request.params.court_id).first()
        return serializer.CourtDetailSerializer(court, request=request).data


@site
class Edit(BookingBase):
    name = "预订修改"

    def get_context(self, request, *args, **kwargs):
        data = dict()
        update_fields = list()
        if self.booking.name != request.params.name:
            data['name'] = {'from': self.booking.name, 'to': request.params.name}
            update_fields.append('name')
            self.booking.name = request.params.name
        if self.booking.description != request.params.description:
            data['description'] = {'from': self.booking.description, 'to': request.params.description}
            update_fields.append('description')
            self.booking.description = request.params.description
        if update_fields:
            with transaction.atomic():
                self.booking.save(force_update=True, update_fields=update_fields)
                models.BookingTrace.objects.create(
                    booking_id=self.booking.pk,
                    user_id=request.user.pk,
                    owner=request.user.pk == self.booking.user_id,
                    type=constants.BookingTraceTypeCode.EDIT.code,
                    data=json.dumps(data, ensure_ascii=False)
                )
        return serializer.BookingDetailSerializer(self.booking, request=request).data

    class Meta:
        param_fields = (
            ('name', fields.CharField(label='名称', max_length=64)),
            ('description', fields.CharField(label='描述', max_length=255, required=False, default="")),
        )


@site
class Cancel(BookingBase):
    name = "关灯操作"

    def get_context(self, request, *args, **kwargs):
        with transaction.atomic():
            court = self.booking.court
            # 连接plc进行关灯
            if config.IS_PRODUCTION:
                try:
                    result = plc.send_message(config.PLC_IP_ADDRESS, court.id, 'OFF', int(config.PLC_TOTAL))
                except:
                    # print(result)
                    raise CoolAPIException(ErrorCode.ERROR_SYSTEM)

                if result[0] != 'SUCCESS' or result[1] != 'OFF':
                    raise CoolAPIException(ErrorCode.ERROR_SYSTEM)

            now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            self.booking.__setattr__('end_time', now),
            # self.booking.__setattr__('delete_status', 1),
            # self.booking.delete()
            self.booking.save(force_update=True, update_fields=['end_time', 'delete_status'])

            # 关灯，把状态置为’0‘
            court.__setattr__('light', 0),
            court.save(force_update=True, update_fields=['light', ])

        return {}

    class Meta:
        param_fields = (

        )


@site
class Caltime(BookingBase):
    name = "获取开灯时长"

    def get_context(self, request, *args, **kwargs):
        now = datetime.datetime.now()
        end_time = now.strftime("%Y-%m-%d %H:%M:%S")
        start_time = self.booking.start_time
        starttime = datetime.datetime.strptime(str(start_time), "%Y-%m-%d %H:%M:%S")
        endtime = datetime.datetime.strptime(str(end_time), "%Y-%m-%d %H:%M:%S")
        c = (endtime - starttime).seconds
        d = 60
        catTime = format(float(c) / float(d), '.2f')
        return {'catTime': catTime, 'costMinute': config.MINUTE_COST, 'freeTime': config.FREE_TIME}


@site
class Config(APIBase):
    name = "获取开灯时长"

    def get_context(self, request, *args, **kwargs):
        return {'isPopup': config.IS_POPUP, 'popupContent': config.POPUP_CONTENT}


@site
class Payment(BookingBase):
    name = "进行预支付操作"

    def get_context(self, request, *args, **kwargs):
        # 前端传送过来的商品id
        total_fee = int(request.query_params.get('total_fee'))
        order_random_string = str(int(time.time()))
        # 获取用户对象，应该是当前登录用户，这里写死了
        # user_object = models.UserInfo.objects.filter(id=1).first()  # user_object.openid
        # 商品的对象
        # goods_object = models.Goods.objects.filter(id=goods_id).first()  # goods_object.price
        # 创建一个订单
        # order_object = models.Order.objects.create(goods=goods_object, user=user_object, uid=order_random_string,
        #                                            status=1)
        order_object = models.Order.objects.create(
            user_id=request.user.pk,
            date=datetime.date.today(),
            pay_status=0,
            uid=order_random_string,
        )
        order_object.save(update_fields=['pay_status', ], force_update=True)
        # 按照微信的规则，去生成支付需要的一大堆的数据
        # https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
        # print(order_random_string)
        # ###################### 1.调用支付统一下单 ######################
        info = {
            # 小程序id T
            'appid': settings.WECHAT_APPID,
            # 商户关联id T
            'mch_id': settings.MCH_ID,
            # 设备号 F
            'device_info': 'ymq-text',
            # 随机字符串 T
            'nonce_str': "".join([chr(random.randint(65, 90)) for _ in range(12)]),
            # 签名类型 F
            'sign_type': "MD5",
            # 商品描述 F
            'body': "支付球场灯光费用",
            # 商品详情 F
            'detail': '根据收费标准，支付场球灯光费用.',
            # 附加数据 F
            'attach': '无',
            # 商户订单号 T
            'out_trade_no': order_random_string,
            # 标价金额 T
            'total_fee': total_fee,  # 总金额
            # 终端IP(用户ip) F
            'spbill_create_ip': request.META.get('REMOTE_ADDR'),
            # # 支付成功之后，微信异步通知(post) T
            'notify_url': "https://wxapp.bgypt.com/api/booking/notify",
            # 交易类型 T
            'trade_type': 'JSAPI',
            # 用户标识(trade_type=JSAPI，此参数必传)
            'openid': request.user.openid
        }
        # 1.1 签名
        #       对字典中的key按照ASCII码从小到大排序
        #       将排完序的值拼接 stringA = appid=wx55cca0b94f723dc7&mch_id=1526049051
        #       让stringA和key拼接：stringSignTemp = stringA+"&key=192006250b4c09247ec02edce69f6a2d" key为商户平台设置的密钥key
        #       MD5(stringSignTemp)
        #       将密文转换为大写
        #       得到签名 sign
        #       把签名再添加到info中    info['sign'] = sign值

        # 商户密钥key
        pay_key = settings.MCH_KEY
        #
        temp = "&".join(["{0}={1}".format(k, info[k]) for k in sorted(info)] + ["{0}={1}".format("key", pay_key, ), ])
        sign = hashlib.md5(temp.encode(encoding='UTF-8')).hexdigest().upper()
        info['sign'] = sign
        # 1.2 向 https://api.mch.weixin.qq.com/pay/unifiedorder 发请求 (json转换为xml)

        xml_string = "<xml>{0}</xml>".format("".join(["<{0}>{1}</{0}>".format(k, v) for k, v in info.items()]))
        prepay = requests.post('https://api.mch.weixin.qq.com/pay/unifiedorder', data=xml_string.encode('utf-8'))
        # 1.3 从结果xml中提取 prepay_id

        root = ET.XML(prepay.content.decode('utf-8'))
        prepay_dict = {child.tag: child.text for child in root}
        prepay_id = prepay_dict['prepay_id']

        # ####################### 2.再次签名 #######################
        info_dict = {
            # 小程序的appid
            'appId': settings.WECHAT_APPID,
            'timeStamp': str(int(time.time())),  # 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间
            'nonceStr': "".join([chr(random.randint(65, 90)) for _ in range(12)]),  # 随机字符串，长度为32个字符以下。
            'package': 'prepay_id={0}'.format(prepay_id),  # 统一下单接口返回的 prepay_id 参数值，提交格式如：prepay_id=*
            'signType': 'MD5',  # 签名类型，默认为MD5，支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
        }
        temp = "&".join(
            ["{0}={1}".format(k, info_dict[k]) for k in sorted(info_dict)] + ["{0}={1}".format("key", pay_key, ), ])
        sign2 = hashlib.md5(temp.encode(encoding='UTF-8')).hexdigest().upper()
        info_dict['paySign'] = sign2

        # print(info_dict)
        return info_dict


@site
class Notify(APIBase):
    def get_context(self, request, *args, **kwargs):
        # 1. 获取结果把结果XML转换为字典格式
        root = ET.XML(request.body.decode('utf-8'))
        result = {child.tag: child.text for child in root}

        # 2. 校验签名是否正确，防止恶意请求。
        sign = result.pop('sign')

        # key为商户平台设置的密钥key
        key = settings.MCH_KEY
        temp = "&".join(
            ["{0}={1}".format(k, result[k]) for k in sorted(result)] + ["{0}={1}".format("key", key, ), ])
        local_sign = hashlib.md5(temp.encode(encoding='UTF-8')).hexdigest().upper()

        # 签名一致
        if local_sign == sign:
            # 根据订单号，把数据库的订单状态修改为支付成功
            out_trade_no = result.get('out_trade_no')
            # 如果支付成功，修改订单状态
            models.Order.objects.filter(uid=out_trade_no).update(pay_status=1)
            response = """<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"""
            return Response(response)

    name = "支付完成后的通知"


urlpatterns = site.urlpatterns
app_name = site.app_name
